/*
 * qv_utils.c
 */

#include "qv_utils.h"
#include "qv_geneticEnvironment.h"
#include "qv_geneticAlgorithm.h"
#include "qv_fileIO.h"


/*
 * Generates the name of a bot, for a specific round, type and id. It
 * will put the result in <botNameBuffer>.
 */
char *generateBotName(int round, char *botType, int id) {
	char *botName = malloc(sizeof(char) * LINE_BUF_SIZE);
	sprintf(botName, "%03d%s%02d", round, botType, id);
	return botName;
}

/*
 * returns "random" number in range [min, max]
 * (NOTE: RAND() USED HERE NOT FROM BG_LIB.C)
 */
float randQV(float min, float max) {
	float fraction = (float) rand() / (float) RAND_MAX;
	return (min + (fraction * (max - min)));
}

/*
 * returns gaussian random number
 */
float randGauss(float mu, float sigma) {
	float u1, u2, s, rv;

	u1 = 0;
	u2 = 0;
	s = 2;

	while (s <= 0 || s > 1) {
		u1 = randQV(-1, 1);
		u2 = randQV(-1, 1);
		s = u1 * u1 + u2 * u2;
	}

	rv = u1 * sqrt((-2 * log(s))/s);
	return rv * sigma + mu;
}

float minQV(float a, float b) {
	if (a < b)
		return a;
	return b;
}

float maxQV(float a, float b) {
	if (a > b)
		return a;
	return b;
}


/*
 * Sets all files in genData to right name according to <botName>
 */
void setFixedFileGens(char *botName, int botIndex, QV_genData *genData) {
	char *weightIFileName = malloc(sizeof(char) * LINE_BUF_SIZE);
	char *weightWFileName = malloc(sizeof(char) * LINE_BUF_SIZE);
	
	sprintf(weightIFileName, "bots/%s_i.c", botName);
	sprintf(weightWFileName, "bots/%s_w.c", botName);

	strcpy(genData[botIndex].basic_gen_data[CHARACTERISTIC_NAME], botName);
	
	if (getSettings()->use_weights) {
		strcpy(genData[botIndex].basic_gen_data[CHARACTERISTIC_ITEMWEIGHTS], weightIFileName);
		strcpy(genData[botIndex].basic_gen_data[CHARACTERISTIC_WEAPONWEIGHTS], weightWFileName);
	}
}


/*
 * creates random value for characteristic corresponding to <botCharNum>
 */
char *getRandomBotCharact(int botCharNum) {
	char *randVal = malloc(sizeof(char) * LINE_BUF_SIZE);
	randVal[0] = '\0';
	
	if (isIntCharacts(botCharNum)) {
		sprintf(randVal, "%d", (rand() % 4000) + 1);
	}
	else if (isFloatCharacts(botCharNum)) {
		switch (botCharNum)
		{
			case CHARACTERISTIC_VIEW_FACTOR:
				snprintf(randVal, 15, "%f", randQV(0.1, 1.0));	break;

			case CHARACTERISTIC_VIEW_MAXCHANGE:
				snprintf(randVal, 15, "%f", randQV(240.0, 360.0)); break;
			
			default:
				snprintf(randVal, 15, "%f", randQV(0.0, 1.0));	break;
		}
	}
	else {
		switch (botCharNum)
		{
			case CHARACTERISTIC_NAME: strcat(randVal, QV_BOT_NAME);					break;
			case CHARACTERISTIC_GENDER:	strcat(randVal, QV_BOT_GENDER);				break;
			case CHARACTERISTIC_WEAPONWEIGHTS: strcat(randVal, QV_BOT_WEAP_FILE);	break;
			case CHARACTERISTIC_CHAT_FILE: strcat(randVal, QV_BOT_CHAT_FILE);		break;
			case CHARACTERISTIC_CHAT_NAME: strcat(randVal, QV_BOT_CHAT_NAME);		break;
			case CHARACTERISTIC_ITEMWEIGHTS: strcat(randVal, QV_BOT_ITEM_FILE);		break;
		}
	}

	return randVal;
}


/*
 * creates random value for characteristic corresponding to <botCharNum>
 */
char *getGaussBotCharact(int botCharNum, char *oldValue) {
	float oldFloat;
	int oldInt;
	float gauss = randGauss(getSettings()->mutation_mu, getSettings()->mutation_sigma);
	char *randVal = malloc(sizeof(char) * LINE_BUF_SIZE);
	randVal[0] = '\0';
	
	if (isIntCharacts(botCharNum)) {
		oldInt = atoi(oldValue);
		sprintf(randVal, "%d", (int)minQV(1, maxQV(4000, oldInt + (gauss * 4000))));
	}
	else if (isFloatCharacts(botCharNum)) {
		oldFloat = atof(oldValue);		
		switch (botCharNum)
		{
			case CHARACTERISTIC_VIEW_FACTOR:
				snprintf(randVal, 15, "%f", minQV(1, maxQV(0.1, oldFloat + gauss)));			break;

			case CHARACTERISTIC_VIEW_MAXCHANGE:
				snprintf(randVal, 15, "%f", minQV(360, maxQV(240, oldFloat + (gauss * 120))));	break;
			
			default:
				snprintf(randVal, 15, "%f", minQV(1, maxQV(0, oldFloat + gauss)));				break;
		}
	}
	else {
		switch (botCharNum)
		{
			case CHARACTERISTIC_NAME: strcat(randVal, QV_BOT_NAME);					break;
			case CHARACTERISTIC_GENDER:	strcat(randVal, QV_BOT_GENDER);				break;
			case CHARACTERISTIC_WEAPONWEIGHTS: strcat(randVal, QV_BOT_WEAP_FILE);	break;
			case CHARACTERISTIC_CHAT_FILE: strcat(randVal, QV_BOT_CHAT_FILE);		break;
			case CHARACTERISTIC_CHAT_NAME: strcat(randVal, QV_BOT_CHAT_NAME);		break;
			case CHARACTERISTIC_ITEMWEIGHTS: strcat(randVal, QV_BOT_ITEM_FILE);		break;
		}
	}

	return randVal;
}

/*
 * Creates random bot characteristics.
 */
void generateRandomGens(QV_genData *genData, int botNum, char* botName) {
	char *randVal;
	int i;
	
	for (i = 0; i < QV_NUM_BOT_CHARS; i++) {
		randVal = getGaussBotCharact(i, getRandomBotCharact(i));
		strcpy(genData[botNum].basic_gen_data[i], randVal);
		free(randVal);
	}
}


/*
 * creates random value for item weights corresponding to <botCharNum>
 */
char *getRandomItemWeight(int botCharNum) {
	char* randVal = malloc(sizeof(char) * LINE_BUF_SIZE);
	
	if (botCharNum == 0)
		sprintf(randVal, "%d", (int)randQV(1.0, 3.0));
	else if (botCharNum == 1)
		sprintf(randVal, "%d", (int)randQV(1.0, 4.0));
	else if (botCharNum >= 2 && botCharNum <= 19) 
	{
		switch(botCharNum) {
			case 2: sprintf(randVal, "%d", (int)(randQV(1.0,2.0)*5)); break;
			case 3: sprintf(randVal, "%d", (int)(randQV(2.0,3.0)*5)); break;
			case 4: sprintf(randVal, "%d", (int)(randQV(4.0,128.0)*5)); break;
			case 5: sprintf(randVal, "%d", (int)(randQV(4.0,6.0)*5)); break;
			case 6: sprintf(randVal, "%d", (int)(randQV(8.0,100.0)*5)); break;
			case 7: sprintf(randVal, "%d", (int)(randQV(16.0,40.0)*5)); break;
			case 8: sprintf(randVal, "%d", (int)(randQV(2.0,80.0)*5)); break;
			case 9: sprintf(randVal, "%d", (int)(randQV(2.0,190.0)*5)); break;
			case 10: sprintf(randVal, "%d", (int)(randQV(2.0,96.0)*5)); break;
			case 11: sprintf(randVal, "%d", (int)(randQV(2.0,55.0)*5)); break;
			default: sprintf(randVal, "%d", (int)(randQV(1.0,190.0)*5)); break;
		}
	}
	else if (botCharNum >= 20 && botCharNum <= 27)
		sprintf(randVal, "%d", (int)(randQV(1.0,40.0)*10));
	else
		sprintf(randVal, "%d", 50);

	return randVal;
}

/*
 * creates random value for item weights corresponding to <botCharNum>
 */
char *getGaussItemWeight(int botCharNum, char *oldValue) {
	int oldInt = atoi(oldValue);
	float gauss = randGauss(getSettings()->mutation_mu, getSettings()->mutation_sigma);
	char* randVal = malloc(sizeof(char) * LINE_BUF_SIZE);
	
	if (botCharNum == 0)
		sprintf(randVal, "%d", (int)minQV(3, maxQV(1, oldInt + (gauss * 2))));
	else if (botCharNum == 1)
		sprintf(randVal, "%d", (int)minQV(4, maxQV(1, oldInt + (gauss * 3))));
	else if (botCharNum >= 2 && botCharNum <= 19) 
	{
		switch(botCharNum) {
			case 2: sprintf(randVal, "%d", (int)minQV(10, maxQV(5, oldInt + (gauss * 5)))); break;
			case 3: sprintf(randVal, "%d", (int)minQV(15, maxQV(10, oldInt + (gauss * 5)))); break;
			case 4: sprintf(randVal, "%d", (int)minQV(615, maxQV(20, oldInt + (gauss * 595)))); break;
			case 5: sprintf(randVal, "%d", (int)minQV(30, maxQV(20, oldInt + (gauss * 10)))); break;
			case 6: sprintf(randVal, "%d", (int)minQV(500, maxQV(40, oldInt + (gauss * 660)))); break;
			case 7: sprintf(randVal, "%d", (int)minQV(200, maxQV(80, oldInt + (gauss * 120)))); break;
			case 8: sprintf(randVal, "%d", (int)minQV(400, maxQV(10, oldInt + (gauss * 390)))); break;
			case 9: sprintf(randVal, "%d", (int)minQV(950, maxQV(10, oldInt + (gauss * 940)))); break;
			case 10: sprintf(randVal, "%d", (int)minQV(480, maxQV(10, oldInt + (gauss * 470)))); break;
			case 11: sprintf(randVal, "%d", (int)minQV(275, maxQV(10, oldInt + (gauss * 265)))); break;
			default: sprintf(randVal, "%d", (int)minQV(950, maxQV(5, oldInt + (gauss * 945)))); break;
		}
	}
	else if (botCharNum >= 20 && botCharNum <= 27)
		sprintf(randVal, "%d", (int)minQV(400, maxQV(10, oldInt + (gauss * 390))));
	else
		sprintf(randVal, "%d", 50);

	return randVal;
}

/*
 * Creates random item weights.
 */
void generateRandomItemWeigths(QV_genData *genData, int botNum) {
	char *randVal;
	int i;
	
	for (i = 0; i < QV_NUM_ITEM_WEIGHTS; i++) {
		randVal = getGaussItemWeight(i, getRandomItemWeight(i));
		strcpy(genData[botNum].item_gen_data[i], randVal);
		free(randVal);
	}
}


/*
 * Writes the data for new bots to bot files
 */
void writeNewBotFiles(QV_genData *newGenData) {
	char **botNormBasicGens;
	char **botNormItemWeightGens;
	int i, j, bots;

	if (getSettings()->testmode)
		bots = getSettings()->testbots;
	else
		bots = getSettings()->bots;

	/* First restore bot entry file */
	restoreBotEntryFile();

	for (i = 0; i < bots; i++) {
		
		if (getSettings()->dependencies) {
			/* Normalize base gen data to write to ai-file */
			botNormBasicGens = normalizeBasicGens(newGenData, i);

			/* Normalize item weight gen data to write to ai-file */
			botNormItemWeightGens = normalizeItemWeightGens(newGenData, i);
		}
		else {
			botNormBasicGens = malloc(sizeof(char*) * QV_NUM_BOT_CHARS);
			for (j = 0; j < QV_NUM_BOT_CHARS; j++) {
				botNormBasicGens[j] = malloc(sizeof(char) * LINE_BUF_SIZE);
				strcpy(botNormBasicGens[j], newGenData[i].basic_gen_data[j]);
			}

			botNormItemWeightGens = malloc(sizeof(char*) * QV_NUM_ITEM_WEIGHTS);
			for (j = 0; j < QV_NUM_ITEM_WEIGHTS; j++) {
				botNormItemWeightGens[j] = malloc(sizeof(char) * LINE_BUF_SIZE);
				strcpy(botNormItemWeightGens[j], newGenData[i].item_gen_data[j]);
			}
		}

		/* Write the ai files using the normalized data */
		writeBotAIFile(botNormBasicGens);
		writeItemWeightsFile(botNormBasicGens[CHARACTERISTIC_NAME], botNormItemWeightGens);

		for (j = 0; j < QV_NUM_BOT_CHARS; j++)
			free(botNormBasicGens[j]);
		free(botNormBasicGens);

		for (j = 0; j < QV_NUM_ITEM_WEIGHTS; j++)
			free(botNormItemWeightGens[j]);
		free(botNormItemWeightGens);
	}
}


/* return whether type of bot characteristic is int */
int isIntCharacts(int botCharNum) {
	return (botCharNum == CHARACTERISTIC_CHAT_CPM);
}

/* return whether type of bot characteristic is float */
int isFloatCharacts(int botCharNum) {
	return (botCharNum == CHARACTERISTIC_ATTACK_SKILL ||
			botCharNum == CHARACTERISTIC_VIEW_FACTOR ||
			botCharNum == CHARACTERISTIC_VIEW_MAXCHANGE ||
			botCharNum == CHARACTERISTIC_REACTIONTIME ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY_SHOTGUN ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY_LIGHTNING ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY_RAILGUN ||
			botCharNum == CHARACTERISTIC_AIM_ACCURACY_BFG10K ||
			botCharNum == CHARACTERISTIC_AIM_SKILL ||
			botCharNum == CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER ||
			botCharNum == CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER ||
			botCharNum == CHARACTERISTIC_AIM_SKILL_PLASMAGUN ||
			botCharNum == CHARACTERISTIC_AIM_SKILL_BFG10K ||
			botCharNum == CHARACTERISTIC_CHAT_INSULT ||
			botCharNum == CHARACTERISTIC_CHAT_MISC ||
			botCharNum == CHARACTERISTIC_CHAT_STARTENDLEVEL ||
			botCharNum == CHARACTERISTIC_CHAT_ENTEREXITGAME ||
			botCharNum == CHARACTERISTIC_CHAT_KILL ||
			botCharNum == CHARACTERISTIC_CHAT_DEATH ||
			botCharNum == CHARACTERISTIC_CHAT_ENEMYSUICIDE ||
			botCharNum == CHARACTERISTIC_CHAT_HITTALKING ||
			botCharNum == CHARACTERISTIC_CHAT_HITNODEATH ||
			botCharNum == CHARACTERISTIC_CHAT_HITNOKILL ||
			botCharNum == CHARACTERISTIC_CHAT_RANDOM ||
			botCharNum == CHARACTERISTIC_CHAT_REPLY ||
			botCharNum == CHARACTERISTIC_CROUCHER ||
			botCharNum == CHARACTERISTIC_JUMPER ||
			botCharNum == CHARACTERISTIC_WALKER ||
			botCharNum == CHARACTERISTIC_WEAPONJUMPING ||
			botCharNum == CHARACTERISTIC_GRAPPLE_USER ||
			botCharNum == CHARACTERISTIC_AGGRESSION ||
			botCharNum == CHARACTERISTIC_SELFPRESERVATION ||
			botCharNum == CHARACTERISTIC_VENGEFULNESS ||
			botCharNum == CHARACTERISTIC_CAMPER ||
			botCharNum == CHARACTERISTIC_EASY_FRAGGER ||
			botCharNum == CHARACTERISTIC_ALERTNESS ||
			botCharNum == CHARACTERISTIC_FIRETHROTTLE);
}

/*
 * return whether type of bot characteristic is string 
 */
int isStringCharacts(int botCharNum) {
	return (botCharNum == CHARACTERISTIC_NAME ||
			botCharNum == CHARACTERISTIC_GENDER ||
			botCharNum == CHARACTERISTIC_WEAPONWEIGHTS ||
			botCharNum == CHARACTERISTIC_CHAT_FILE ||
			botCharNum == CHARACTERISTIC_CHAT_NAME ||
			botCharNum == CHARACTERISTIC_ITEMWEIGHTS);
}
